<?php
namespace com\rs\dns\service;

use com\rs\dns\constant\RecordConstant;
use com\rs\dns\controller\api\vo\DomainInfo;
use com\rs\dns\controller\api\vo\RecordBalanceForm;
use com\rs\dns\controller\api\vo\RecordForm;
use com\rs\dns\controller\api\vo\RecordInfo;
use com\rs\dns\controller\api\vo\RecordQuery;
use com\rs\dns\exception\BaException;
use com\rs\dns\repository\RrRepository;
use com\rs\dns\repository\SoaRepository;
use com\rs\dns\repository\UrlRepository;
use restphp\biz\PageParam;
use restphp\utils\RestClassUtils;
use restphp\utils\RestStringUtils;

/**
 * Class RecordService
 * @package com\rs\dns\service
 */
final class RecordService {
    /**
     * 获取记录管理列表.
     * @param RecordQuery $recordQuery
     * @param PageParam $pageParam
     * @return \restphp\biz\RestPageReturn
     */
    public static function getRecordList($recordQuery, $pageParam, $username = '') {
        $arrRule = array(
            'a.isurl' => 0
        );
        if (!RestStringUtils::isBlank($recordQuery->getDomain())) {
            $arrRule['b.origin'] = $recordQuery->getDomain() . ".";
        }
        if (!RestStringUtils::isBlank($recordQuery->getUsername())) {
            $arrRule['b.username'] = $recordQuery->getUsername();
        }
        if (!RestStringUtils::isBlank($username)) {
            $arrRule['b.username'] = $username;
        }

        $arrParams = array(
            'table' => ' rr a left join soa b on a.zone=b.id left join net c on a.netid=c.id ',
            'output' => ' a.*, b.origin, b.username, c.netname ',
            'rule' => $arrRule,
            'page_param' => $pageParam,
            'order' => ' order by a.name ',
            'natural' => true
        );
        $rrRepository = new RrRepository();
        return $rrRepository->getPageReturn($arrParams, new RecordInfo());
    }

    /**
     * 添加记录值.
     * @param RecordForm $recordForm
     * @return void
     * @throws \restphp\exception\RestException
     */
    public static function add($recordForm) {
        $rrRepository = new RrRepository();
        $domainInfo = DomainService::getInfoByDomain($recordForm->getOrigin() . ".");
        $arrExistsRule = array(
            'zone' => $domainInfo['id'],
            'name' => $recordForm->getName(),
            'type' => $recordForm->getType(),
            'data' => $recordForm->getData(),
            'netid' => $recordForm->getNetid()
        );
        $arrExists = $rrRepository->findOne($arrExistsRule);
        if (!empty($arrExists)) {
            throw new BaException(RecordConstant::RECORD_SAME_EXISTS);
        }

        //排除转发
        $arrExistsRule = array(
            'zone' => $domainInfo['id'],
            'name' => $recordForm->getName(),
            'type' => 'A',
            'netid' => $recordForm->getNetid(),
            'isurl' => 1
        );
        $arrExists = $rrRepository->findOne($arrExistsRule);
        if (!empty($arrExists)) {
            throw new BaException(RecordConstant::RECORD_SAME_URL_EXISTS);
        }

        $arrAdd = RestClassUtils::beanToArr($recordForm, false);
        unset($arrAdd['origin']);
        $arrAdd['[zone]'] = $domainInfo['id'];
        $rrRepository->insert($arrAdd);

        //更新soa版本
        DomainService::updateSerial($domainInfo['id']);

        //数据订阅
        $readData = RestClassUtils::beanToArr($recordForm, false);
        $readData['id'] = $rrRepository->last_insert_id();
        ForeignService::post("/records", $readData);
    }

    /**
     * 修改记录.
     * @param int $id
     * @param RecordForm $recordForm
     * @throws \restphp\exception\RestException
     */
    public static function modify($id, $recordForm) {
        $arrUpdate = RestClassUtils::beanToArr($recordForm, false);
        unset($arrUpdate['origin']);
        $arrRule = array(
            'id' => $id
        );
        $rrRepository = new RrRepository();
        $arrInfo = $rrRepository->findOne($arrRule);
        if (null == $arrInfo) {
            throw new BaException(RecordConstant::RECORD_NOT_FOUND);
        }

        $arrExistsRule = array(
            'id' => array(
                'id <> ' . $id
            ),
            'zone' => $arrInfo['zone'],
            'name' => $recordForm->getName(),
            'type' => $recordForm->getType(),
            'data' => $recordForm->getData(),
            'netid' => $recordForm->getNetid()
        );
        $arrExists = $rrRepository->findOne($arrExistsRule);
        if (!empty($arrExists)) {
            throw new BaException(RecordConstant::RECORD_SAME_EXISTS);
        }

        //排除转发
        $arrExistsRule = array(
            'zone' => $arrInfo['zone'],
            'name' => $recordForm->getName(),
            'type' => 'A',
            'netid' => $recordForm->getNetid(),
            'isurl' => 1
        );
        $arrExists = $rrRepository->findOne($arrExistsRule);
        if (!empty($arrExists)) {
            throw new BaException(RecordConstant::RECORD_SAME_URL_EXISTS);
        }


        $rrRepository->update($arrUpdate, $arrRule);

        //更新soa版本
        DomainService::updateSerial($arrInfo['zone']);

        //数据订阅
        ForeignService::put("/records/{$id}", RestClassUtils::beanToArr($recordForm, false));
    }

    /**
     * 删除。
     * @param $id
     * @throws \restphp\exception\RestException
     */
    public static function delete($id) {
        //获取域名id
        $arrDomainId = self::getDomainIdById(array($id));

        //删除记录
        $arrRule = array(
            'id' => $id
        );
        $rrRepository = new RrRepository();
        $rrRepository->delete($arrRule);

        //更新soa版本
        foreach ($arrDomainId as $zone) {
            DomainService::updateSerial($zone);
        }


        //数据订阅
        ForeignService::delete("/records/{$id}");
    }

    /**
     * 批量删除.
     * @param $arrId
     * @throws \restphp\exception\RestException
     */
    public static function batchDelete($arrId) {
        //获取域名id
        $arrDomainId = self::getDomainIdById($arrId);

        //删除记录
        $rrRepository = new RrRepository();
        $rrRepository->batchDelete($arrId);

        //更新soa版本
        foreach ($arrDomainId as $zone) {
            DomainService::updateSerial($zone);
        }

        //数据订阅
        ForeignService::post("/records/actions/delete", $arrId);
    }

    /**
     * 获取域名ID列表
     * @param $arrId
     * @return array|null
     * @throws \restphp\exception\RestException
     */
    public static function getDomainIdById($arrId) {
        $strC = '';
        foreach ($arrId as $id) {
            $strC .= ('' == $strC ? '' : ',') . '?';
        }
        $arrRule = array(
            'id' => array(
                " id in ($strC)",
                $arrId
            )
        );
        $rrRepository = new RrRepository();
        $arrRr = $rrRepository->findAll($arrRule);
        if (!empty($arrRr)) {
            $arrReturn = array();
            foreach ($arrRr as $item) {
                if (!in_array($item['zone'], $arrReturn)) {
                    $arrReturn[] = $item['zone'];
                }
            }
            return $arrReturn;
        }
        return null;
    }

    /**
     * 通过域名删除
     * @param array $arrZoneId
     */
    public static function deleteByZones($arrZoneId) {
        $rrRepository = new RrRepository();
        $rrRepository->batchDelete($arrZoneId, 'zone');
    }

    /**
     * 获取负载均衡列表.
     * @param $id
     * @return array
     * @throws BaException
     * @throws \restphp\exception\RestException
     */
    public static function getBalanceList($id) {
        $arrInfoRule = array(
            'id' => $id
        );
        $rrRepository = new RrRepository();
        $arrInfo = $rrRepository->fineOne($arrInfoRule);
        if (null == $arrInfo) {
            throw new BaException(RecordConstant::RECORD_NOT_FOUND);
        }

        $arrParams = array(
            'table' => ' rr a left join soa b on a.zone=b.id left join net c on a.netid=c.id ',
            'output' => ' a.*, b.origin, b.username, c.netname ',
            'rule' => array(
                'zone' => $arrInfo['zone'],
                'name' => $arrInfo['name'],
                'type' => $arrInfo['type']
            ),
            'order' => ' order by a.name '
        );

        $infoList = $rrRepository->select($arrParams);
        $recordInfoList = array();
        foreach ($infoList as $recordInfo) {
            $recordInfoList[] = RestClassUtils::copyFromArr(new RecordInfo(), $recordInfo);
        }

        return $recordInfoList;
    }

    /**
     * 设置负载均衡.
     * @param RecordBalanceForm $balanceForm
     */
    public static function setBalance($balanceForm) {
        $rrRepository = new RrRepository();
        foreach ($balanceForm->getAuxList() as $item) {
             $arrUpdate = array(
                'aux' => $item['aux'],
                'balance' => $balanceForm->getBalance()
            );
            $arrRule = array(
                'id' => $item['id']
            );

            $rrRepository->update($arrUpdate, $arrRule);
        }
    }

    /**
     * 解析记录类型统计.
     * @return array
     */
    public static function statisticsType() {
        $arrType = array(
            'A',
            'MX',
            'CNAME',
            'NS',
            'PTR',
            'TXT',
            'AAAA',
            'SRV'
        );
        $rrRepository = new RrRepository();
        $data = array();
        $total = 0;
        foreach ($arrType as $type) {
            $num = $rrRepository->countByMixRule(array(
                'type' => $type
            ));
            $data[$type] = $num;
            $total += $num;
        }
        $urlRepository = new UrlRepository();
        //隐性转发
        $hiddenUrl = $urlRepository->countByMixRule(array(
            'URLtype' => 1
        ));
        $data['隐性转发'] = $hiddenUrl;
        //显性转发
        $showUrl = $urlRepository->countByMixRule(array(
            'URLtype' => 0
        ));
        $data['显性转发'] = $showUrl;
        $total += $hiddenUrl + $showUrl;
        $data['total'] = $total;
        return $data;
    }

    /**
     * 解析记录类型统计.
     * @param $username
     * @return array
     */
    public static function statisticsUser($username) {
        $arrType = array(
            'A',
            'MX',
            'CNAME',
            'NS',
            'PTR',
            'TXT',
            'AAAA',
            'SRV'
        );
        $rrRepository = new RrRepository();
        $data = array();
        $total = 0;
        foreach ($arrType as $type) {
            $arrParams = array(
                'table' => ' rr a left join soa b on a.zone=b.id ',
                'rule' => array(
                    'a.type' => $type,
                    'b.username' => $username
                )
            );
            $num = $rrRepository->count($arrParams);
            $data[$type] = $num;
            $total += $num;
        }
        $urlRepository = new UrlRepository();
        //隐性转发
        $hiddenUrl = $urlRepository->count(array(
            'table' => ' url a left join soa b on a.zone=b.id ',
            'rule' => array(
                'a.URLtype' => 1,
                'b.username' => $username
            )
        ));
        $data['隐性转发'] = $hiddenUrl;
        //显性转发
        $showUrl = $urlRepository->count(array(
            'table' => ' url a left join soa b on a.zone=b.id ',
            'rule' => array(
                'a.URLtype' => 0,
                'b.username' => $username
            )
        ));
        $data['显性转发'] = $showUrl;
        $total += $hiddenUrl + $showUrl;
        $data['total'] = $total;
        return $data;
    }

    /**
     * 是否有记录权限.
     * @param $recordId
     * @param $username
     * @return bool
     */
    public static function hadRecord($recordId, $username) {
        $arrParams = array(
            'table' => ' rr a left join soa b on a.zone=b.id ',
            'rule' => array(
                'a.id' => $recordId,
                'b.username' => $username
            )
        );
        $rrRepository = new RrRepository();
        return $rrRepository->count($arrParams) > 0;
    }

    /**
     * 是否有记录(S)权限.
     * @param $arrRecordId
     * @param $username
     * @return bool
     */
    public static function hadRecords($arrRecordId, $username) {
        $strRule = '';
        foreach ($arrRecordId as $id) {
            $strRule .= ('' == $strRule ? '' : ',') . '?';
        }
        $arrParams = array(
            'table' => ' rr a left join soa b on a.zone=b.id ',
            'rule' => array(
                'a.id' => array(
                    " a.id in ({$strRule}) ",
                    $arrRecordId
                ),
                'b.username' => $username
            )
        );
        $rrRepository = new RrRepository();
        $total = $rrRepository->count($arrParams);
        return $total == count($arrRecordId);
    }
}